#ifndef _MOE_NSGA_II
#define _MOE_NSGA_II

#include "engine/MOEA.hh"
#include "engine/Random.hh"
#include <float.h>

#include <iostream>


using namespace std;

/**Implements the Non Sorted Genetic Algorithm version 2*/
class NSGAII: public MOEA {

private:

    static const double INFINITE = 1e14;
    /**Data class that contains, solution, the rank value and the distance value, according to
      *the deb's paper
      */
    class Rank {
    private:
        Solution *solution;
        int value;
        int index;
        double distance;

    public:

        Rank() {
            value = 0;
            index = 0;
            distance = 0.0;
        }

        Rank(Solution *solution, int value, int index) {
            this->value = value;
            this->index = index;
            this->solution = solution;
            this->distance = 0;
        }

        Rank(const Rank &rank) {
            value = rank.value;
            index = rank.index;
            solution = rank.solution;
            distance = rank.distance;

        }
        ~Rank() {}

        Rank& operator=(const Rank& rank) {
            value = rank.value;
            index = rank.index;
            solution = rank.solution;
            distance = rank.distance;
            return *this;
        }

        void setDistance(double distance) {
            this->distance = distance;
        }

        double getDistance() {
            return distance;
        }

        int getValue() {
            return value;
        }

        void setValue(int value) {
            this->value = value;
        }

        int getIndex() {
            return index;
        }

        Solution *getSolution() {
            return solution;
        }

        void setSolution(Solution *solution) {
            this->solution = solution;
        }

    };
    /**Has the implementation of methods for compare Ranks in order to sort, used by NSGAII*/
    class RankMObjectiveComparator {
    public:
        static int mObjective;

        /**compares the values of the objective pointed by mObjective @see ArrayList::sort*/
        static int mCompare(const void * o1, const void *o2) {
            double m1 = (*(Rank**)o1)->getSolution()->getObjectiveValue(mObjective);
            double m2 = (*(Rank**)o2)->getSolution()->getObjectiveValue(mObjective);

            if (m1 < m2)
                return -1;

            else if (m1 == m2)
                return 0;

            else
                return 1;

        }

        /** acording to the <_n comparision defined in deb paper @see ArrayList::sort
        */
        static int compare(const void * o1, const void *o2) {

            Rank *r1 = (*(Rank**)o1);
            Rank *r2 = (*(Rank**)o2);

            if (r1->getValue() == r2->getValue() &&
                    r1->getDistance() == r2->getDistance())
                return 0;

            else if (r1->getValue() > r2->getValue())
                return 1;

            else if (r1->getValue() == r2->getValue() &&
                     r1->getDistance() < r2->getDistance())
                return 1;

            else
                return -1;

        }
    };

private:


public:

    NSGAII(Population *current,
           ArrayList<Crossover> *crossovers,
           ArrayList<Migration> *migrations,
           ArrayList<Selection> *selections,
           ArrayList<Mutation> *mutations,
           float crossoverProbability);

    NSGAII();


protected:

    void generation();

    void initialize();

    void finalize();


private:

    void show(ArrayList<Solution>* list);

    ArrayList<Solution> *makeOffsprings();

    ArrayList<ArrayList<Rank> >* fastNonDominatedSort(ArrayList<Rank> *population);

    void crowdingDistanceAssignment(ArrayList<Rank> *set);

    ArrayList<Solution> *oneStep(ArrayList<Solution> *solutions);

};
#endif
